﻿using System.Web.Mvc;

namespace LogikBug.ModularMvc.Navigation
{
	public class MenuNode
	{
		#region Fields

		private const string OpenTagFormat = "<{0}>";
		private const string CloseTagFormat = "</{0}>";
		private const string SelfClosingTagFormat = "<{0} />";

		private readonly string _nodeName;
		private readonly MenuNode _parentNode;

		#endregion

		#region Constructors

		static MenuNode()
		{
			A = new MenuNode(NodeNames.ANode);
			Li = new MenuNode(NodeNames.LINode);
			Ul = new MenuNode(NodeNames.ULNode);
			Div = new MenuNode(NodeNames.DivNode);
		}

		public MenuNode(string nodeName)
		{
			_nodeName = nodeName;
		}

		public MenuNode(string nodeName, MenuNode parentNode) : this(nodeName)
		{
			_parentNode = parentNode;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Represents the HTML div tag.
		/// </summary>
		public static MenuNode Div { get; private set; }

		/// <summary>
		/// Represents the HTML ul tag.
		/// </summary>
		public static MenuNode Ul { get; private set; }

		/// <summary>
		/// Represents the HTML li tag.
		/// </summary>
		public static MenuNode Li { get; private set; }

		/// <summary>
		/// Represents the HTML anchor tag.
		/// </summary>
		public static MenuNode A { get; private set; }

		/// <summary>
		/// The opening tag for the current HTML element.
		/// </summary>
		public string OpenTag
		{
			get
			{
				return string.Format(
					IsSelfClosing
						? SelfClosingTagFormat
						: OpenTagFormat,
					_nodeName
				);
			}
		}

		/// <summary>
		/// The closing tag for the current HTML element.
		/// </summary>
		public string CloseTag
		{
			get
			{
				return string.Format(
					IsSelfClosing
						? SelfClosingTagFormat
						: CloseTagFormat,
					_nodeName
				);
			}
		}

		/// <summary>
		/// The HTML element name. (eg. a, li, ul)
		/// </summary>
		public string TagName
		{
			get
			{
				return _nodeName;
			}
		}

		/// <summary>
		/// Determines if the HTML element is self closing.
		/// </summary>
		public virtual bool IsSelfClosing
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Represents the parent HTML tag.
		/// </summary>
		public MenuNode ParentTag
		{
			get
			{
				return _parentNode;
			}
		}

		/// <summary>
		/// The parent opening tag for the current HTML element.
		/// </summary>
		public string ParentOpenTag
		{
			get
			{
				if (_parentNode == null)
				{
					return string.Empty;
				}

				return _parentNode.OpenTag;
			}
		}

		/// <summary>
		/// The parent closing tag for the current HTML element.
		/// </summary>
		public string ParentCloseTag
		{
			get
			{
				if (_parentNode == null)
				{
					return string.Empty;
				}

				return _parentNode.CloseTag;
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Returns a formated string of the current tag. (eg. &lt;ul&gt;{0}&lt;/ul&gt;)
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return string.Format("{0}{1}{2}", OpenTag, "{0}", CloseTag);
		}

		public virtual string ToString(string innerHtml)
		{
			return ToString(MvcHtmlString.Create(innerHtml));
		}

		public virtual string ToString(Menu menu)
		{
			return ToString(menu.ToMvcHtmlString());
		}

		public virtual string ToString(MvcHtmlString innerHtml)
		{
			return string.Format(
				"{0}{1}{2}",
				OpenTag,
				innerHtml.ToHtmlString(),
				CloseTag
			);
		}

		public static MenuNode Create(MenuNode parent)
		{
			switch (parent.TagName)
			{
				case NodeNames.LINode:
				case NodeNames.ANode:
				{
					return new MenuNode(NodeNames.ULNode, parent);
				}
				case NodeNames.DivNode:
				{
					return new MenuNode(NodeNames.DivNode, parent);
				}
				default:
				{
					return new MenuNode(NodeNames.LINode, parent);
				}
			}
		}

		#endregion

		#region Types

		protected struct NodeNames
		{
			public const string ANode = "a";
			public const string LINode = "li";
			public const string ULNode = "ul";
			public const string DivNode = "div";
		}

		#endregion
	}
}
